{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# TDC 105: Metric Functions and Oracles \n",
    "\n",
    "[Tianfan Fu, Wenhao Gao]\n",
    "\n",
    "Welcome to the TDC community! \n",
    "\n",
    "### What is molecular generation? \n",
    "\n",
    "Molecular generation is one of the most fundamental challenges in drug discovery that is to identify one or more molecules with a specific set of properties of interest, such as binding affinity and drug-likeness. Generative algorithms, compared to high-throughput (virtual) screening which is to experimentally (computationally) evaluate every candidate in a library, have an advantage of exploring the chemical space selectively and avoiding enumerating the whole chemical space. Further, the generative algorithms can explore chemical space beyond the limited beginning pool and provide novel chemical structures with preferential intellectual property positions, whereas molecules in screening are often pre-existing. Current generative algorithm can be divided into two classes: distribution learning and goal-directed generation. Distribution learning models are meant to interpolate within a chemical space comprised of a training set of molecules and to generate new molecules with similar properties (e.g., variational encoder, generative adversarial network). Goal-directed generations try to generate new molecules that maximize an oracle function (e.g., generich algorithm, reinforcement learning). \n",
    "\n",
    "\n",
    "### What is metric function? \n",
    "\n",
    "A metric function is to evaluate the output of distribution learning. In distribution learning tasks, we approximate an unknown distribution $p(x)$ with some distribution $q(x)$ based on a set of molecules sampled from $p(x)$. A well-trained model generates diverse and unique molecules that can be used for building a tailored virtual library. Thus metric functions evaluate the quality of a batch of molecules and the deviation between $p(x)$ and $q(x)$. Note our current metric functions are adopted from MOSES benchmark.\n",
    "\n",
    "\n",
    "### What is oracle? \n",
    "\n",
    "An oracle is a black box that evaluates the ground truth score for input molecule(s). In goal-directed molecular generation tasks, a computational scoring function is usually used as a surrogate oracle to assess how much the molecules fulfill the requirements, such as inhibiting (e.g., Glycogen Synthase Kinase 3 Beta, GSK3B) or drug-likeliness (e.g., QED). The objective of the goal-directed molecular generation is to propose molecules maximizing such scores.\n",
    "\n",
    "In this tutorial, we will cover the basics of TDC oracle and after this tutorial, you will be able to leverage most of the useful oracles supported!\n",
    "\n",
    "We assume you have familiarize yourself with the installations and data loaders. If not, please visit [TDC 101 Data Loaders](https://github.com/mims-harvard/TDC/blob/master/tutorials/TDC_101_Data_Loader.ipynb) first!\n",
    "\n",
    "We will first introduce metric functions, then introduce oracles and some advanced usage of oracles.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Import the oracle"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "from tdc import Oracle"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Molecular Property Oracle\n",
    "\n",
    "It includes:\n",
    "    \n",
    "* `QED` is an indicator of drug-likeness, ranging from 0 to 1. \n",
    "\n",
    "* `Penalized LogP` is a logP score that accounts for ring size and synthetic accessibility\n",
    "\n",
    "* `DRD2` measures a molecule's biological activity against a biological target named the dopamine type 2 receptor (DRD2)\n",
    "\n",
    "* `GSK3` measures a molecular's biological activity against GSK3β. \n",
    "\n",
    "* `JNK3` measures a molecular's biological activity against JNK3. \n",
    "\n",
    "* `SA` measures a molecular's synthetic accessibility. \n",
    "\n",
    "We also includes the oracles from [GuacaMol](https://github.com/BenevolentAI/guacamol), it can be divided into several categories:\n",
    "\n",
    "### similarity\n",
    "\n",
    "* `aripiprazole_similarity` measures a molecule's Tanimoto similarity with Aripiprazole. \n",
    "\n",
    "* `albuterol_similarity` measures a molecule's Tanimoto similarity with Albuterol. \n",
    "\n",
    "* `mestranol_similarity` measures a molecule's Tanimoto similarity with Mestranol. \n",
    "\n",
    "### rediscovery\n",
    "\n",
    "* `celecoxib_rediscovery` measures a molecule's Tanimoto similarity with celecoxib's SMILES to check whether it could be rediscovered. \n",
    "\n",
    "* `troglitazone_rediscovery` measures a molecule's Tanimoto similarity with troglitazone's SMILES to check whether it could be rediscovered. \n",
    "\n",
    "* `thiothixene_rediscovery` measures a molecule's Tanimoto similarity with thiothixene's SMILES to check whether it could be rediscovered. \n",
    "\n",
    "\n",
    "### isomer\n",
    "\n",
    "* `isomers_c7h8n2o2` measures a molecule's similarity in terms of atom counter to C7H8N2O2. \n",
    "\n",
    "\n",
    "* `isomers_c9h10n2o2pf2cl` measures a molecule's similarity in terms of atom counter to C9H10N2O2PF2Cl. \n",
    "\n",
    "\n",
    "\n",
    "### MPO (multiproperty objective)\n",
    "\n",
    "* `osimertinib_mpo` measures the geometric means of several scores, including the molecule's Tanimoto similarity to osimertinib, TPSA score and LogP score. \n",
    "\n",
    "* `fexofenadine_mpo` measures the geometric means of several scores, including the molecule's Tanimoto similarity to fexofenadine, TPSA score and LogP score. \n",
    "\n",
    "* `ranolazine_mpo` measures the geometric means of several scores, including the molecule's Tanimoto similarity to ranolazine, TPSA score LogP score and number of fluorine atoms. \n",
    "\n",
    "* `perindopril_mpo` measures the geometric means of several scores, including the molecule's Tanimoto similarity to perindopril and number of aromatic rings. \n",
    "\n",
    "* `amlodipine_mpo` measures the geometric means of several scores, including the molecule's Tanimoto similarity to amlodipine and number rings. \n",
    "\n",
    "* `sitagliptin_mpo` measures the geometric means of several scores, including the molecule's Tanimoto similarity to sitagliptin, TPSA score, LogP score and isomer score with C16H15F6N5O. \n",
    "\n",
    "* `zaleplon_mpo` measures the geometric means of several scores, including the molecule's Tanimoto similarity to zaleplon and isomer score with C19H17N3O2. \n",
    "\n",
    "\n",
    "### median molecule\n",
    "\n",
    "* `median1` measures the average score of the molecule's Tanimoto similarity to Camphor and Menthol. \n",
    "\n",
    "* `median2` measures the average score of the molecule's Tanimoto similarity to Tadalafil and sildenafil. \n",
    "\n",
    "\n",
    "### others \n",
    "\n",
    "* `valsartan_smarts` measures the arithmetic means of several scores, including (1) binary score about whether contain a certain SMARTS structure (2) LogP, (3) TPSA and (4) Bertz score.  \n",
    "\n",
    "* `deco_hop` measures the arithmetic means of several scores, including (1) binary score about whether contain certain SMARTS structures and the molecule's Tanimoto similarity to PHCO. \n",
    "\n",
    "* `scaffold_hop` measures the arithmetic means of several scores, including (1) binary score about whether contain certain SMARTS structures and the molecule's Tanimoto similarity to PHCO. \n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "TDC's oracles can take in either a list of SMILES strings or a single SMILES string. As an example, we define a list of SMILES string and a single SMILES string below:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "smiles_lst = ['CC(C)(C)[C@H]1CCc2c(sc(NC(=O)COc3ccc(Cl)cc3)c2C(N)=O)C1', \\\n",
    "\t\t\t  'C[C@@H]1CCc2c(sc(NC(=O)c3ccco3)c2C(N)=O)C1', \\\n",
    "\t\t\t  'CCNC(=O)c1ccc(NC(=O)N2CC[C@H](C)[C@H](O)C2)c(C)c1', \\\n",
    "\t\t\t  'C[C@@H]1CCN(C(=O)CCCc2ccccc2)C[C@@H]1O']\n",
    "\n",
    "\n",
    "osimertinib_smiles = 'COc1cc(N(C)CCN(C)C)c(NC(=O)C=C)cc1Nc2nccc(n2)c3cn(C)c4ccccc34'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For some oracles, we need to use load well-trained machine learning model to evaluate the score, e.g., \n",
    "\n",
    "* `DRD2`\n",
    "\n",
    "* `GNK3`\n",
    "\n",
    "* `JNK3`\n",
    "\n",
    "We download the model in the first time calling it, then save it as local copy. After that, we load the local copy when calling it. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Found local copy...\n"
     ]
    }
   ],
   "source": [
    "gsk3 = Oracle(name = 'GSK3')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For other oracles, the score can be evaluated using some deterministic rules, e.g.,\n",
    "\n",
    "* `QED`\n",
    "\n",
    "* `Penalized LogP`\n",
    "\n",
    "All the guacamol related oracles belong to this category. \n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "qed = Oracle(name = 'QED')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Oracles can be called to evaluate a single SMILES. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.54"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "gsk3(osimertinib_smiles)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "0.3105348061023151"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "qed(osimertinib_smiles)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Oracles can be called to evaluate a list of SMILES. The output is a list of float. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[0.03, 0.02, 0.0, 0.0]"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "gsk3(smiles_lst)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Adcanced Usage of Oracle\n",
    "\n",
    "We also provide more flexibility interface to some oracles for advanced usaers, so that they can build their own oracle functions:\n",
    "\n",
    "* `isomer_meta` measures a molecule's similarity to a target molecular formula (e.g., C7H8N2O2). \n",
    "\n",
    "* `rediscovery_meta` measures a molecule's Tanimoto similarity with the target SMILES to check whether it could be rediscovered. \n",
    "\n",
    "* `similarity_meta` measures a molecule's Tanimoto similarity with the target SMILES.\n",
    "\n",
    "* `median_meta` measures the average score of a molecule's similarity to two target SMILES. \n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(I) \"Isomer scoring for C7H8N2O2\" using TDC built-in function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3.758742514056854e-48\n"
     ]
    }
   ],
   "source": [
    "isomer_c7h8n2o2 = Oracle(name = 'Isomer_C7H8N2O2')\n",
    "print(isomer_c7h8n2o2(osimertinib_smiles))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "(II) Specify another \"Isomer scoring for C7H8N2O2\" using Meta Oracle. Note that the input to `target_smiles` should be a chemical formula. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3.758742514056854e-48\n"
     ]
    }
   ],
   "source": [
    "isomers_c7h8n2o2_1 = Oracle(name = 'Isomer_Meta', target_smiles = 'C7H8N2O2')\n",
    "print(isomers_c7h8n2o2_1(osimertinib_smiles))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We see they generate the same results, confirming the correctness of meta oracles. As another example, we have meta median oracle, where we can specify two molecules that we want to simultaneously be similar to. You can also specify the fingerprint methods, the score modifier functions, and the aggregate mean function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.1407807656815755\n"
     ]
    }
   ],
   "source": [
    "median2 = Oracle(name = 'median2')\n",
    "print(median2(osimertinib_smiles))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.1407807656815755\n"
     ]
    }
   ],
   "source": [
    "tadalafil_smiles = 'O=C1N(CC(N2C1CC3=C(C2C4=CC5=C(OCO5)C=C4)NC6=C3C=CC=C6)=O)C'\n",
    "sildenafil_smiles = 'CCCC1=NN(C2=C1N=C(NC2=O)C3=C(C=CC(=C3)S(=O)(=O)N4CCN(CC4)C)OCC)C'\n",
    "median2_2 = Oracle(name = 'Median_Meta', \n",
    "                   target_smiles = (tadalafil_smiles, sildenafil_smiles), \n",
    "                   fp1 = 'ECFP6', \n",
    "                   fp2 = 'ECFP6', \n",
    "                   modifier_func1 = None, \n",
    "                   modifier_func2 = None, \n",
    "                   means = 'geometric')\n",
    "\n",
    "\n",
    "print(median2_2(osimertinib_smiles))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We see again the meta oracle generates the same results as the built-in functions, confirming the correctness.\n",
    "\n",
    "That's it for this tutorial! Hope you can now leverage TDC for molecule generations!\n",
    "\n",
    "If you haven't, do checkout all the previous tutorials:\n",
    "\n",
    "* [TDC 101 Data Loader](https://github.com/mims-harvard/TDC/blob/master/tutorials/TDC_101_Data_Loader.ipynb)\n",
    "\n",
    "* [TDC 102 Data Functions](https://github.com/mims-harvard/TDC/blob/master/tutorials/TDC_102_Data_Functions.ipynb)\n",
    "\n",
    "* [TDC 103 Part 1: Datasets - Small Molecules](https://github.com/mims-harvard/TDC/blob/master/tutorials/TDC_103.1_Datasets_Small_Molecules.ipynb)\n",
    "\n",
    "* [TDC 103 Part 2: Datasets - Biologics](https://github.com/mims-harvard/TDC/blob/master/tutorials/TDC_103.2_Datasets_Biologics.ipynb)\n",
    "\n",
    "* [TDC 104 ML Model Examples with DeepPurpose](https://github.com/mims-harvard/TDC/blob/master/tutorials/TDC_104_ML_Model_DeepPurpose.ipynb)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
